﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Xml;
using System.Xml.XPath;
using System.IO;
using ComponentFactory.Krypton.Toolkit;
using System.Management;
using Mp3Lib;
using System.Text.RegularExpressions;
using mshtml;

namespace MP3YearFinder
{
    public enum FileColumns
    {
        Track = 0,
        Year = 1,
        NewYear = 2,
        Title = 3,
        Artist = 4,
        FileName = 5
    }

    public partial class frmMain : Form
    {
        string m_startPath;
        KryptonBreadCrumbItem m_SelectedItem;
        bool m_ignoreEvents = false;
        bool m_FilesModified = false;

        #region Initialisation

        public frmMain(string startPath)
        {
            InitializeComponent();

            m_startPath = startPath;
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            crumb.RootItem.ShortText = "Computer";

            const int Removable = 2;
            const int LocalDisk = 3;
            const int Network = 4;
            const int CD = 5;

            ManagementObjectCollection queryCollection = getDrives();

            foreach (ManagementObject mo in queryCollection)
            {
                switch (int.Parse(mo["DriveType"].ToString()))
                {
                    case Removable: //removable drives 
                        break;
                    case LocalDisk: //Local drives 
                        break;
                    case CD: //CD rom drives 
                        break;
                    case Network: //Network drives 
                        break;
                    default: //defalut to folder 
                        break;
                }

                string desc = "";
                if (mo["ProviderName"] != null)
                {
                    desc = mo["ProviderName"].ToString();
                }
                else
                {
                    if (mo["VolumeName"] != null)
                    {
                        desc = mo["VolumeName"].ToString();
                    }
                }

                if (desc == "")
                    desc = mo["Name"].ToString();
                else
                    desc = desc + " (" + mo["Name"].ToString() + ")";

                //create new drive node
                KryptonBreadCrumbItem item = new KryptonBreadCrumbItem(desc);
                item.Tag = mo["Name"].ToString() + "\\";
                crumb.RootItem.Items.Add(item);
            }

            WebPageNavigate(webPageHTM, "about:blank");
            //webPageHTM.Navigate("about:blank");
            webPageHTM.Document.Write("WikiPedia related pages will display here");

            CreateFileListViewHeaders();

            toolCmdAccept.Text = "Accept";
            toolCmdAccept.Enabled = false;

            SetCrumbPath();
        }

        #endregion

        #region Form Events

        private void cmdOK_Click(object sender, EventArgs e)
        {
            UpdateMP3files();
            this.Close();
        }

        private void listViewFiles_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (m_ignoreEvents) return;

            listViewFiles.Refresh();

            if (listViewFiles.SelectedItems.Count == 1)
            {
                toolLblYear.Enabled = true;
                toolTxtYear.Enabled = true;
                toolCmdOK.Enabled = true;
                GetSearchResults(listViewFiles.SelectedItems[0], false);
            }
            else
            {
                toolLblYear.Enabled = false;
                toolTxtYear.Enabled = false;
                toolCmdOK.Enabled = false;
                WebPageNavigate(webPageHTM, "about:blank");
            }
        }

        private void grdSearchResults_RowEnter(object sender, DataGridViewCellEventArgs e)
        {
            if (m_ignoreEvents) return;

            toolCmdAccept.Text = "Accept";
            toolCmdAccept.Enabled = false;

            if (listViewFiles.SelectedItems.Count == 1)
            {
                if (grdSearchResults.SelectedRows.Count == 1)
                    GetSelectedPage(listViewFiles.SelectedItems[0], listViewFiles.SelectedItems[0].SubItems[(int)FileColumns.Artist].Text, grdSearchResults.SelectedRows[0].Cells["Entry"].Value.ToString(), false);
                else
                    WebPageNavigate(webPageHTM, "about:blank");
            }
            else
                WebPageNavigate(webPageHTM, "about:blank");
        }

        private void cmdScan_Click(object sender, EventArgs e)
        {
            m_ignoreEvents = true;

            pgrProgress.Value = 0;
            pgrProgress.Maximum = listViewFiles.Items.Count;
            pgrProgress.Visible = true;
            grdSearchResults.RowCount = 0;

            WebPageNavigate(webPageHTM, "about:blank");
            webPageHTM.Refresh();
            Refresh();

            for (int i = 0; i < listViewFiles.Items.Count; i++)
            {
                ListViewItem lvi = listViewFiles.Items[i];

                GetSelectedPage(lvi, lvi.SubItems[(int)FileColumns.Artist].Text, lvi.SubItems[(int)FileColumns.Title].Text, true);
                
                pgrProgress.Value += 1;
            }

            grdSearchResults.RowCount = 0;
            WebPageNavigate(webPageHTM, "about:blank");

            pgrProgress.Visible = false;
            m_ignoreEvents = false;

            MessageBox.Show("Completed full scan.", "Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void toolAccept_Click(object sender, EventArgs e)
        {
            UpdateSelectedYear(((ToolStripItem)sender).Tag.ToString());
        }

        private void toolTxtYear_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Return)
                UpdateSelectedYear(toolTxtYear.Text.ToString());
        }

        private void toolCmdOK_Click(object sender, EventArgs e)
        {
            UpdateSelectedYear(toolTxtYear.Text.ToString());
        }

        private void crumb_SelectedItemChanged(object sender, EventArgs e)
        {
            if (!m_ignoreEvents)
            {
                if (m_FilesModified)
                {
                    switch (MessageBox.Show("Do you want to save the changes you have made in this folder?", "Save Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
                    {
                        case System.Windows.Forms.DialogResult.Yes:
                            UpdateMP3files();
                            break;
                        case System.Windows.Forms.DialogResult.No:
                            m_FilesModified = false;
                            break;
                        case System.Windows.Forms.DialogResult.Cancel:
                            m_ignoreEvents = true;
                            crumb.SelectedItem = m_SelectedItem;
                            m_ignoreEvents = false;
                            return;
                    }
                }
            }

            KryptonBreadCrumbItem item = crumb.SelectedItem;

            if (item == null) return;
            if (item.Tag == null) return;

            if (!m_ignoreEvents)
                PaintFileList((string)crumb.SelectedItem.Tag);

            item.Items.Clear();

            string[] dirs = Directory.GetDirectories(item.Tag.ToString());

            for (int i = 0; i < dirs.Length; i++)
            {
                string name = Path.GetFileName(dirs[i]);
                KryptonBreadCrumbItem item2 = new KryptonBreadCrumbItem(name);
                item2.Tag = dirs[i];
                item.Items.Add(item2);
            }

            m_SelectedItem = item;
        }

        private void toolCmdFind_Click(object sender, EventArgs e)
        {
            FindOnPage(toolTxtFind.Text);
        }

        private void toolTxtFind_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Return)
                FindOnPage(toolTxtFind.Text);
        }

        #endregion

        #region Methods

        private void SetCrumbPath()
        {
            m_ignoreEvents = true;

            if (m_startPath != "")
            {
                string[] folders = m_startPath.Split('\\');

                for (int i = 0; i < folders.Length; i++)
                {
                    string file = folders[i];

                    if (crumb.SelectedItem.Items[file] != null)
                        crumb.SelectedItem = crumb.SelectedItem.Items[file];
                    else
                    {
                        // Try to find the path by looping through the tags
                        for (int j = 0; j < crumb.SelectedItem.Items.Count; j++)
                        {
                            KryptonBreadCrumbItem item = crumb.SelectedItem.Items[j];
                            if (((string)item.Tag).ToLower() == ((string)(file + "\\")).ToLower())
                                crumb.SelectedItem = crumb.SelectedItem.Items[j];
                        }
                    }

                    if ((i+2) == folders.Length) m_ignoreEvents = false;
                }
            }

            m_ignoreEvents = false;
        }

        private void CreateFileListViewHeaders()
        {
            ColumnHeader colHead;

            colHead = new ColumnHeader();
            colHead.Text = "Track";
            listViewFiles.Columns.Add(colHead);

            colHead = new ColumnHeader();
            colHead.Text = "Year";
            colHead.TextAlign = HorizontalAlignment.Right;
            listViewFiles.Columns.Add(colHead);

            colHead = new ColumnHeader();
            colHead.Text = "New Year";
            colHead.TextAlign = HorizontalAlignment.Right;
            listViewFiles.Columns.Add(colHead);

            colHead = new ColumnHeader();
            colHead.Text = "Title";
            listViewFiles.Columns.Add(colHead);

            colHead = new ColumnHeader();
            colHead.Text = "Artist";
            listViewFiles.Columns.Add(colHead);

            colHead = new ColumnHeader();
            colHead.Text = "Album";
            listViewFiles.Columns.Add(colHead);

            colHead = new ColumnHeader();
            colHead.Text = "Filename";
            listViewFiles.Columns.Add(colHead);
        }

        public string Soundex(string word)
        {
            return Soundex(word, 6);
        }

        public string Soundex(string word, int length)
        {
            // Value to return
            string value = "";
            // Size of the word to process
            int size = word.Length;

            // Make sure the word is at least two characters in length
            if (size > 1)
            {
                // Convert the word to all uppercase
                word = word.ToUpper();
                // Convert the word to character array for faster processing
                char[] chars = word.ToCharArray();
                // Buffer to build up with character codes
                StringBuilder buffer = new StringBuilder();
                buffer.Length = 0;
                // The current and previous character codes
                int prevCode = 0;
                int currCode = 0;
                // Append the first character to the buffer
                buffer.Append(chars[0]);
                // Loop through all the characters and convert them to the proper character code
                for (int i = 1; i < size; i++)
                {
                    switch (chars[i])
                    {
                        case 'A':       currCode = 0; break;
                        case 'E':       currCode = 0; break;
                        case 'I':       currCode = 0; break;
                        case 'O':       currCode = 0; break;
                        case 'U':       currCode = 0; break;
                        case 'H':       currCode = 0; break;
                        case 'W':       currCode = 0; break;
                        case 'Y':       currCode = 0; break;
                        case 'B':       currCode = 1; break;
                        case 'F':		currCode = 1; break;
                        case 'P':		currCode = 1; break;
                        case 'V':		currCode = 1; break;
                        case 'C':		currCode = 2; break;
                        case 'G':		currCode = 2; break;
                        case 'J':		currCode = 2; break;
                        case 'K':		currCode = 2; break;
                        case 'Q':		currCode = 2; break;
                        case 'S':		currCode = 2; break;
                        case 'X':		currCode = 2; break;
                        case 'Z':		currCode = 2; break;
                        case 'D':		currCode = 3; break;
                        case 'T':		currCode = 3; break;
                        case 'L':		currCode = 4; break;
                        case 'M':		currCode = 5; break;
                        case 'N':		currCode = 5; break;
                        case 'R':		currCode = 6; break;
                    }

                    // Check to see if the current code is the same as the last one
                    if (currCode != prevCode)
                    {
                        // Check to see if the current code is 0 (a vowel); do not process vowels
                        if (currCode != 0)
                            buffer.Append(currCode);
                    }
                    // Set the new previous character code
                    prevCode = currCode;
                    // If the buffer size meets the length limit, then exit the loop
                    if (buffer.Length == length)
                        break;
                }

                // Pad the buffer, if required
                size = buffer.Length;
                if (size < length)
                    buffer.Append('0', (length - size));

                // Set the value to return
                value = buffer.ToString();
            }
            // Return the value
            return value;
        }

        private void UpdateSelectedYear(string newYear)
        {
            ListViewItem lvi = listViewFiles.SelectedItems[0];

            UpdateSelectedYear(lvi, newYear);
        }

        private void UpdateSelectedYear(ListViewItem lvi, string newYear)
        {
            lvi.SubItems[(int)FileColumns.NewYear].Text = newYear;

            if (lvi.SubItems[(int)FileColumns.NewYear].Text != lvi.SubItems[(int)FileColumns.Year].Text)
            {
                m_FilesModified = true;
                lvi.UseItemStyleForSubItems = false;
                lvi.SubItems[(int)FileColumns.NewYear].BackColor = Color.Yellow;
            }
            else
            {
                lvi.UseItemStyleForSubItems = true;
                lvi.SubItems[(int)FileColumns.NewYear].BackColor = Color.FromArgb(255, 255, 255, 255);
            }
        }

        private void UpdateMP3files()
        {
            // Save all the changes back to the mp3 files
            for (int i = 0; i < listViewFiles.Items.Count; i++)
            {
                ListViewItem lvi = listViewFiles.Items[i];

                if (lvi.SubItems[(int)FileColumns.NewYear].Text != "" &&
                    lvi.SubItems[(int)FileColumns.Year].Text != lvi.SubItems[(int)FileColumns.NewYear].Text)
                {
                    Mp3File mp3File = null;

                    try
                    {
                        mp3File = new Mp3File(lvi.Tag.ToString());
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString(), "Error Reading Tag");
                        return;
                    }

                    mp3File.TagHandler.Year = lvi.SubItems[(int)FileColumns.NewYear].Text;
                    mp3File.Update();
                }
            }

            m_FilesModified = false;
        }

        #endregion

        #region Breadcrumb and filelist methods

        protected ManagementObjectCollection getDrives()
        {
            //get drive collection 
            ManagementObjectSearcher query = new
                ManagementObjectSearcher("SELECT * From Win32_LogicalDisk");
            ManagementObjectCollection queryCollection = query.Get();
            return queryCollection;
        }

        private void PaintFileList(string root)
        {
            try
            {
                ListViewItem lvi;

                if (root.CompareTo("") == 0)
                    return;

                DirectoryInfo dir = new DirectoryInfo(root);

                FileInfo[] files = dir.GetFiles("*.mp3");

                listViewFiles.Items.Clear();

                listViewFiles.BeginUpdate();

                foreach (FileInfo fi in files)
                {
                    Mp3File mp3File = null;

                    try
                    {
                        mp3File = new Mp3File(fi);
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.ToString(), "Error Reading Tag");
                        return;
                    }

                    lvi = new ListViewItem();
                    lvi.Text = mp3File.TagHandler.Track;
                    lvi.ImageIndex = 1;
                    lvi.Tag = fi.FullName;

                    lvi.SubItems.AddRange(new string[] { mp3File.TagHandler.Year, "", mp3File.TagHandler.Title, mp3File.TagHandler.Artist, mp3File.TagHandler.Album, mp3File.FileName });

                    listViewFiles.Items.Add(lvi);
                }
                listViewFiles.EndUpdate();

                listViewFiles.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
                listViewFiles.Columns[2].AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
            }
            catch (System.Exception err)
            {
                MessageBox.Show("Error: " + err.Message);
            }
        }

        #endregion

        #region WikiPedia methods

        private void GetSearchResults(ListViewItem lvi, bool batchMode)
        {
            string search = "";
            Uri searchURIXML;
            Uri searchURI;
            HttpWebRequest mySearch;
            bool ok = false;

            // WEB SEARCH
            // http://en.wikipedia.org/w/index.php?title=Special%3ASearch&search=Belinda+Carlisle+We+Want+The+Same+Thing
            // http://en.wikipedia.org/w/index.php?title=Special%3ASearch&search=Meat+Loaf+Rock+And+Roll+Dreams+Come+Through

            // API SEARCH
            // http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=Belinda+Carlisle+We+Want+The+Same+Thing&srwhat=text&format=xml
            // http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=Meat+Loaf+Rock+And+Roll+Dreams+Come+Through&srwhat=text&format=xml

            string title = lvi.SubItems[(int)FileColumns.Title].Text;
            string artist = lvi.SubItems[(int)FileColumns.Artist].Text;

            string searchURLXML = "http://en.wikipedia.org/w/api.php?action=query&list=search&srwhat=text&srlimit=10&format=xml&srsearch=";
            string searchURL = "http://en.wikipedia.org/wiki/Special:Search?search=";

            // First try to match on track name followed by (song)
            search = title.Replace(" ", "_") + "_(song)";

            searchURIXML = new Uri(searchURLXML + search);
            searchURI = new Uri(searchURL + search);

            mySearch = (HttpWebRequest)WebRequest.Create(searchURIXML);
            mySearch.Credentials = System.Net.CredentialCache.DefaultCredentials;
            mySearch.Accept = "text/xml";
            mySearch.UserAgent = "IE";

            using (HttpWebResponse response = (HttpWebResponse)mySearch.GetResponse())
            {
                string ResponseText;
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    ResponseText = reader.ReadToEnd();

                    XmlDocument xml = new XmlDocument();
                    xml.LoadXml(ResponseText);

                    XmlNodeList xmlNodes = xml.DocumentElement.GetElementsByTagName("p");

                    if (xmlNodes.Count > 0)
                    {
                        if (xmlNodes[0].Attributes["title"].Value.ToLower().Replace(" ", "_") == search.ToLower())
                            ok = true;
                    }

                    if (ok)
                    {
                        grdSearchResults.RowCount = 0;

                        if (batchMode)
                            grdSearchResults.RowCount = 1;
                        else
                            grdSearchResults.RowCount = xmlNodes.Count;

                        for (int i = 0; i < grdSearchResults.RowCount; i++)
                        {
                            grdSearchResults.Rows[i].Cells["Entry"].Value = xmlNodes[i].Attributes["title"].Value;
                            grdSearchResults.Rows[i].Cells["Summary"].Value = Regex.Replace(xmlNodes[i].Attributes["snippet"].Value, @"<(.|\n)*?>", string.Empty);
                            grdSearchResults.Rows[i].Cells["Date"].Value = xmlNodes[i].Attributes["timestamp"].Value;
                        }
                    }
                }
            }


            // Now try to match on song name followed by (artist song)
            if (!ok)
            {
                search = title.Replace(" ", "_") + "_(" + artist.Replace(" ", "_") + "_song)";

                searchURIXML = new Uri(searchURLXML + search);
                searchURI = new Uri(searchURL + search);

                mySearch = (HttpWebRequest)WebRequest.Create(searchURIXML);
                mySearch.Credentials = System.Net.CredentialCache.DefaultCredentials;
                mySearch.Accept = "text/xml";
                mySearch.UserAgent = "IE";

                using (HttpWebResponse response = (HttpWebResponse)mySearch.GetResponse())
                {
                    string ResponseText;
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        ResponseText = reader.ReadToEnd();

                        XmlDocument xml = new XmlDocument();
                        xml.LoadXml(ResponseText);

                        XmlNodeList xmlNodes = xml.DocumentElement.GetElementsByTagName("p");

                        if (xmlNodes.Count > 0)
                        {
                            if (xmlNodes[0].Attributes["title"].Value.ToLower().Replace(" ", "_") == search.ToLower())
                                ok = true;
                        }

                        if (ok)
                        {
                            grdSearchResults.RowCount = 0;

                            if (batchMode)
                                grdSearchResults.RowCount = 1;
                            else
                                grdSearchResults.RowCount = xmlNodes.Count;

                            for (int i = 0; i < grdSearchResults.RowCount; i++)
                            {
                                grdSearchResults.Rows[i].Cells["Entry"].Value = xmlNodes[i].Attributes["title"].Value;
                                grdSearchResults.Rows[i].Cells["Summary"].Value = Regex.Replace(xmlNodes[i].Attributes["snippet"].Value, @"<(.|\n)*?>", string.Empty);
                                grdSearchResults.Rows[i].Cells["Date"].Value = xmlNodes[i].Attributes["timestamp"].Value;
                            }
                        }
                    }
                }
            }


            // Now try to match on just the song name
            if (!ok)
            {
                search = title.Replace(" ", "+");

                searchURIXML = new Uri(searchURLXML + search);
                searchURI = new Uri(searchURL + search);

                mySearch = (HttpWebRequest)WebRequest.Create(searchURIXML);
                mySearch.Credentials = System.Net.CredentialCache.DefaultCredentials;
                mySearch.Accept = "text/xml";
                mySearch.UserAgent = "IE";

                using (HttpWebResponse response = (HttpWebResponse)mySearch.GetResponse())
                {
                    string ResponseText;
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        ResponseText = reader.ReadToEnd();

                        XmlDocument xml = new XmlDocument();
                        xml.LoadXml(ResponseText);

                        XmlNodeList xmlNodes = xml.DocumentElement.GetElementsByTagName("p");

                        if (xmlNodes.Count > 0)
                        {
                            if (xmlNodes[0].Attributes["title"].Value.ToLower().Replace(" ", "+") == search.ToLower())
                                ok = true;
                        }

                        if (ok)
                        {
                            grdSearchResults.RowCount = 0;

                            if (batchMode)
                                grdSearchResults.RowCount = 1;
                            else
                                grdSearchResults.RowCount = xmlNodes.Count;

                            for (int i = 0; i < grdSearchResults.RowCount; i++)
                            {
                                grdSearchResults.Rows[i].Cells["Entry"].Value = xmlNodes[i].Attributes["title"].Value;
                                grdSearchResults.Rows[i].Cells["Summary"].Value = Regex.Replace(xmlNodes[i].Attributes["snippet"].Value, @"<(.|\n)*?>", string.Empty);
                                grdSearchResults.Rows[i].Cells["Date"].Value = xmlNodes[i].Attributes["timestamp"].Value;
                            }
                        }
                    }
                }
            }


            // Now try to match on just the song name and artist
            if (!ok)
            {
                search = title.Replace(" ", "+") + "+" + artist.Replace(" ", "+");

                searchURIXML = new Uri(searchURLXML + search);
                searchURI = new Uri(searchURL + search);

                mySearch = (HttpWebRequest)WebRequest.Create(searchURIXML);
                mySearch.Credentials = System.Net.CredentialCache.DefaultCredentials;
                mySearch.Accept = "text/xml";
                mySearch.UserAgent = "IE";

                using (HttpWebResponse response = (HttpWebResponse)mySearch.GetResponse())
                {
                    string ResponseText;
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        ResponseText = reader.ReadToEnd();

                        XmlDocument xml = new XmlDocument();
                        xml.LoadXml(ResponseText);

                        XmlNodeList xmlNodes = xml.DocumentElement.GetElementsByTagName("p");

                        grdSearchResults.RowCount = 0;

                        if (batchMode)
                            grdSearchResults.RowCount = 1;
                        else
                            grdSearchResults.RowCount = xmlNodes.Count;

                        for (int i = 0; i < grdSearchResults.RowCount; i++)
                        {
                            grdSearchResults.Rows[i].Cells["Entry"].Value = xmlNodes[i].Attributes["title"].Value;
                            grdSearchResults.Rows[i].Cells["Summary"].Value = Regex.Replace(xmlNodes[i].Attributes["snippet"].Value, @"<(.|\n)*?>", string.Empty);
                            grdSearchResults.Rows[i].Cells["Date"].Value = xmlNodes[i].Attributes["timestamp"].Value;
                        }
                    }
                }
            }

            if (!batchMode)
            {
                grdSearchResults.Refresh();

                if (grdSearchResults.SelectedRows.Count == 1)
                    GetSelectedPage(lvi, lvi.SubItems[(int)FileColumns.Artist].Text, grdSearchResults.SelectedRows[0].Cells["Entry"].Value.ToString(), batchMode);
                else
                    WebPageNavigate(webPageHTM, "about:blank");
            }
        }

        private string GetPageName(string searchArtist, string searchSong)
        {
            bool ok = false;
            string result = "";

            // API
            // http://en.wikipedia.org/w/api.php?action=opensearch&search=walking_on_sunshine_song&format=xml
            /*
            <?xml version="1.0" ?> 
            <SearchSuggestion version="2.0" xmlns="http://opensearch.org/searchsuggest2">
                <Query xml:space="preserve">walking_on_sunshine_song</Query> 
                <Section>
                    <Item>
                        <Text xml:space="preserve">Walking on Sunshine (song)</Text> 
                        <Description xml:space="preserve">"Walking on Sunshine" is a song written by Kimberley Rew for Katrina and the Waves' 1983 eponymous debut full-length album.</Description> 
                        <Url xml:space="preserve">http://en.wikipedia.org/wiki/Walking_on_Sunshine_(song)</Url> 
                    </Item>
                </Section>
            </SearchSuggestion>
            */

            string loadURL = "http://en.wikipedia.org/w/api.php?action=opensearch&format=xml&search=";
            string search = searchSong.Replace(" ", "_") + "_song";

            XmlDocument xmlResult = GetXMLResults(loadURL + search);
            XmlNodeList data = xmlResult.DocumentElement.GetElementsByTagName("Text");

            if (data.Count > 0)
            {
                if ((searchSong + " (song)").ToLower() == data[0].InnerText.ToLower())
                {
                    ok = true;
                    result = data[0].InnerText.Replace(" ", "_");
                }
            }

            if (!ok)
            {
                search = searchSong.Replace(" ", "_") + "_" + searchArtist.Replace(" ", "_") + "_song";

                xmlResult = GetXMLResults(loadURL + search);
                data = xmlResult.DocumentElement.GetElementsByTagName("Text");

                if (data.Count > 0)
                {
                    if ((searchSong + " (" + searchArtist + " song)").ToLower() == data[0].InnerText.ToLower())
                    {
                        ok = true;
                        result = data[0].InnerText.Replace(" ", "_");
                    }
                }
            }

            if (!ok)
            {
                search = searchSong.Replace(" ", "_");

                xmlResult = GetXMLResults(loadURL + search);
                data = xmlResult.DocumentElement.GetElementsByTagName("Text");

                if (data.Count > 0)
                {
                    if (searchSong.ToLower() == data[0].InnerText.ToLower())
                    {
                        ok = true;
                        result = data[0].InnerText.Replace(" ", "_");
                    }
                }
            }

            if (!ok)
            {
                // Do a search using (because this finds similar words):
                // http://en.wikipedia.org/w/api.php?action=query&list=search&format=xml&srsearch=Belinda+Carlisle+We+Want+The+Same+Thing&srwhat=text
                loadURL = "http://en.wikipedia.org/w/api.php?action=query&list=search&format=xml&srsearch=";
                search = searchSong.Replace(" ", "_") + "_song";

                xmlResult = GetXMLResults(loadURL + search);
                data = xmlResult.DocumentElement.GetElementsByTagName("p");

                if (data.Count > 0)
                {
                    if (Soundex(data[0].Attributes["title"].Value.ToLower()) == Soundex(search.ToLower().Replace("_", " ")))
                    {
                        ok = true;
                        result = data[0].Attributes["title"].Value;
                    }
                }
            }

            return result;
        }

        private bool GetSelectedPage(ListViewItem lvi, string searchArtist, string searchSong, bool batchMode)
        {
            bool ok = false;
            bool artistMatch = false;
            Match releaseYear = null;
            string titleLoad = null;

            // API RETRIEVE PAGE IN XML
            // http://en.wikipedia.org/wiki/Special:Export/(We_Want)_The_Same_Thing
            // http://en.wikipedia.org/wiki/Special:Export/Rock_and_Roll_Dreams_Come_Through

            string loadURLXML = "http://en.wikipedia.org/wiki/Special:Export/";
            string loadURL = "http://en.wikipedia.org/wiki/";


            if (batchMode)
                titleLoad = GetPageName(searchArtist, searchSong);
            else
                titleLoad = searchSong.Replace(" ", "_");

            if (titleLoad!=null && titleLoad != "")
            {
                Uri loadURIXML = new Uri(loadURLXML + titleLoad);
                Uri loadURI = new Uri(loadURL + titleLoad);

                if (!batchMode) WebPageNavigate(webPageHTM, loadURI);

                XmlDocument xmlResult = GetXMLResults(loadURIXML.OriginalString);
                XmlNodeList data = xmlResult.DocumentElement.GetElementsByTagName("text");

                if (data.Count == 1)
                {
                    string pageText = data[0].InnerText;
                    string searchText = "";

                    pageText = pageText.Replace("{{Start date", "Start date");
                    pageText = pageText.Replace("{{start date", "start date");

                    string[] infoPanels = pageText.Split(new string[] { "{{" }, StringSplitOptions.RemoveEmptyEntries);

                    // Loop round each result of ds
                    foreach (string infobox in infoPanels)
                    {
                        searchText = String.Empty;
                        artistMatch = false;
                        releaseYear = null; 

                        if (infobox.ToLower().Contains("song infobox")) searchText = "song infobox";
                        if (infobox.ToLower().Contains("infobox song")) searchText = "infobox song";
                        if (infobox.ToLower().Contains("infobox single")) searchText = "infobox single";
                        if (infobox.ToLower().Contains("single infobox")) searchText = "single infobox";

                        if (searchText != String.Empty)
                        {
                            try
                            {
                                string[] songInfo = infobox.Split(new string[] { "\n|" }, StringSplitOptions.RemoveEmptyEntries);

                                foreach (string element in songInfo)
                                {
                                    if (element.Trim().Length > 7 && element.Trim().Substring(0, 7).ToLower() == "artist ")
                                    {
                                        string artist = element.Substring(element.IndexOf("=") + 1);

                                        if (artist.ToLower().Contains(searchArtist.ToLower()))
                                            artistMatch = true;

                                        if (!artistMatch)
                                        {
                                            artist = artist.Replace("&", "and");
                                            artist = artist.Replace("é", "e");
                                            artist = artist.Replace("á", "a");
                                            artist = artist.Replace("Á", "A");
                                            artist = artist.Replace("É", "E");
                                            
                                            if (artist.ToLower().Contains(searchArtist.ToLower()))
                                                artistMatch = true;

                                            artist = artist.Replace("and", "&");
                                            artist = artist.Replace("And", "&");
                                            artist = artist.Replace("AND", "&");

                                            if (artist.ToLower().Contains(searchArtist.ToLower()))
                                                artistMatch = true;
                                        }
                                    }

                                    if (element.Trim().Length > 9 && element.Trim().Substring(0, 9).ToLower() == "released ")
                                    {
                                        string releaseDate = element.Substring(element.IndexOf("=") + 1);
                                        releaseYear = Regex.Match(releaseDate, "\\d{4}");
                                    }

                                    if (artistMatch && releaseYear != null) break;
                                }

                                if (artistMatch && releaseYear != null && releaseYear.ToString() != "")
                                {
                                    if (batchMode)
                                    {
                                        UpdateSelectedYear(lvi, releaseYear.ToString());
                                    }
                                    else
                                    {
                                        toolCmdAccept.Text = "Accept " + releaseYear.ToString();
                                        toolCmdAccept.Tag = releaseYear.ToString();
                                        toolCmdAccept.Enabled = true;
                                    }

                                    ok = true;
                                }

                            }
                            catch (Exception ex)
                            {
#if DEBUG
                                MessageBox.Show(ex.ToString());
#endif
                            }
                        }

                        if (artistMatch && releaseYear != null) break;
                    }
                }

                /*
                    * NICE CODE TO TRANSFORM XML USING MS STANDARD XSL
                // Display the XML formatted
                System.Xml.Xsl.XslCompiledTransform xTrans = new System.Xml.Xsl.XslCompiledTransform();
                xTrans.Load("defaultss.xslt");
                StringReader sr = new StringReader(ResponseText);
                System.Xml.XmlReader xReader = System.Xml.XmlReader.Create(sr);

                MemoryStream ms = new MemoryStream();
                xTrans.Transform(xReader, null, ms);
                ms.Position = 0;

                webPageXML.DocumentStream = ms;
                */
            }

            return ok;
        }

        private XmlDocument GetXMLResults(string Url)
        {
            string responseText;

            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(Url);
            myRequest.Credentials = System.Net.CredentialCache.DefaultCredentials;
            myRequest.Accept = "text/xml";
            myRequest.UserAgent = "IE";

            using (HttpWebResponse response = (HttpWebResponse)myRequest.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    responseText = reader.ReadToEnd();
                }
            }

            // Parse the results
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(responseText);

            return xml;
        }

        private void WebPageNavigate(WebBrowser control, string url)
        {
            Uri loadURI = new Uri(url);
            WebPageNavigate(control, loadURI);
        }

        private void WebPageNavigate(WebBrowser control, Uri uri)
        {
            if (control.IsBusy) control.Stop();

            if (uri.OriginalString.ToLower() == "about:blank")
                control.Navigate("about:blank");
            else
                control.Navigate(uri);

            while (control.IsBusy)
            {
                System.Threading.Thread.Sleep(150);
            }

            if (uri.OriginalString.ToLower() == "about:blank")
            {
                control.Document.OpenNew(true);
                control.Document.Write("&nbsp;");
            }
        }

        #endregion

        #region WebBrowser methods

        //private bool m_textIsNew = true;
        private IHTMLTxtRange m_lastRange;

        private void FindOnPage(string searchText)
        {
            if (Body != null)
            {
                IHTMLTxtRange range = Body.createTextRange();
                
                /*
                if (!m_textIsNew)
                {
                    m_lastRange.moveStart("word", 1);
                    m_lastRange.setEndPoint("EndToEnd", range);
                    range = m_lastRange;
                }
                */

                if (range.findText(searchText, 0, 0))
                {
                    try
                    {
                        range.select();
                        m_lastRange = range;
                        //m_textIsNew = false;
                    }
                    catch (System.Runtime.InteropServices.COMException)
                    {
                        // don't know what to do
                    }
                }
            }
        }

        private DispHTMLDocument Document
        {
            get
            {
                try
                {
                    if (webPageHTM.Document != null)
                    {
                        return (DispHTMLDocument)webPageHTM.Document.DomDocument;
                    }
                }
                catch (InvalidCastException)
                {
                    // nothing to do        
                } return null;
            }
        }

        private DispHTMLBody Body
        {
            get
            {
                if ((Document != null) && (Document.body != null))
                {
                    return (DispHTMLBody)Document.body;
                }
                else
                {
                    return null;
                }
            }
        }
        
        #endregion
    }
}
